﻿using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace Oxage.Converters
{
	//Experimental, not for production use
	public class SVGConverter : IFileConverter
	{
		#region Public methods
		public string SVGToGCode(string svg)
		{
			svg = svg.Replace("xmlns=", "xmlns:ignorexmlns=");

			var doc = XDocument.Parse(svg);
			if (doc.Root == null || doc.Root.Name != "svg")
			{
				throw new Exception("Not a SVG file!");
			}

			//TODO: Culture invariant
			var builder = new StringBuilder();

			FindAllElementsRecursive(doc.Root, element =>
			{
				Match m = null;
				double x = 0;
				double y = 0;
				switch (element.Name.LocalName)
				{
					case "rect":
						double width = 0;
						string w = element.Attribute("width") != null ? element.Attribute("width").Value : "";
						double.TryParse(w, NumberStyles.Any, CultureInfo.InvariantCulture, out width);

						double height = 0;
						string h = element.Attribute("height") != null ? element.Attribute("height").Value : "";
						double.TryParse(h, NumberStyles.Any, CultureInfo.InvariantCulture, out height);

						//double x = 0;
						string xs = element.Attribute("x") != null ? element.Attribute("x").Value : "";
						double.TryParse(xs, NumberStyles.Any, CultureInfo.InvariantCulture, out x);

						//double y = 0;
						string ys = element.Attribute("y") != null ? element.Attribute("y").Value : "";
						double.TryParse(ys, NumberStyles.Any, CultureInfo.InvariantCulture, out y);

						//builder.Append("G89").AppendLine(); //Not sure if this code changes to relative positioning
						builder.AppendFormat("; rectangle" /*: x={0} y={1} w={2} h={3}", x, y, width, height */).AppendLine();
						builder.AppendFormat("G00 X{0} Y{1}", x.ToString(CultureInfo.InvariantCulture), y.ToString(CultureInfo.InvariantCulture)).AppendLine();
						builder.AppendFormat("G01 X{0} Y{1}", (x + width).ToString(CultureInfo.InvariantCulture), y.ToString(CultureInfo.InvariantCulture)).AppendLine();
						builder.AppendFormat("G01 X{0} Y{1}", (x + width).ToString(CultureInfo.InvariantCulture), (y + height).ToString(CultureInfo.InvariantCulture)).AppendLine();
						builder.AppendFormat("G01 X{0} Y{1}", x.ToString(CultureInfo.InvariantCulture), (y + height).ToString(CultureInfo.InvariantCulture)).AppendLine();
						builder.AppendFormat("G01 X{0} Y{1}", x.ToString(CultureInfo.InvariantCulture), y.ToString(CultureInfo.InvariantCulture)).AppendLine();
						builder.AppendLine();
						break;

					case "path":
						string d = element.Attribute("d") != null ? element.Attribute("d").Value : "";
						builder.AppendFormat("; path" /* : d={0}", d */).AppendLine();

						//Matches a segment
						var matches = Regex.Matches(d, @"(?<segment>(?<code>\w)[\d\-\+\.\s,]+)", RegexOptions.IgnoreCase);
						foreach (Match match in matches)
						{
							string code = match.Groups["code"].Value;
							string segment = match.Groups["segment"].Value;

							//SVG: http://apike.ca/prog_svg_paths.html
							//SVG: http://www.w3schools.com/svg/svg_path.asp
							switch (code.ToUpper())
							{
								case "M": //Move to x,y
									m = Regex.Match(segment, @"(?<x>[\d\-\.]+)[\s,]+(?<y>[\d\-\.]+)");
									if (m.Success)
									{
										double.TryParse(m.Groups["x"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out x);
										double.TryParse(m.Groups["y"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out y);
										builder.AppendFormat("G00 X{0} Y{1} ; move", x.ToString(CultureInfo.InvariantCulture), y.ToString(CultureInfo.InvariantCulture)).AppendLine();
									}
									break;

								case "L": //Line to x,y
									m = Regex.Match(segment, @"(?<x>[\d\-\.]+)[\s,]+(?<y>[\d\-\.]+)");
									if (m.Success)
									{
										double.TryParse(m.Groups["x"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out x);
										double.TryParse(m.Groups["y"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out y);
										builder.AppendFormat("G01 X{0} Y{1} ; line", x.ToString(CultureInfo.InvariantCulture), y.ToString(CultureInfo.InvariantCulture)).AppendLine();
									}
									break;

								case "H": //Horizontal line
									m = Regex.Match(segment, @"(?<x>[\d\-\.]+)");
									if (m.Success)
									{
										double.TryParse(m.Groups["x"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out x);
										builder.AppendFormat("G01 X{0} ; horizontal line", x.ToString(CultureInfo.InvariantCulture)).AppendLine(); //TODO: Y
									}
									break;

								case "V": //Vertical line
									m = Regex.Match(segment, @"(?<y>[\d\-\.]+)");
									if (m.Success)
									{
										double.TryParse(m.Groups["y"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out y);
										builder.AppendFormat("G01 Y{0} ; vertical line", y.ToString(CultureInfo.InvariantCulture)).AppendLine(); //TODO: X
									}
									break;

								case "C":
									break;

								case "A":
									//SVG: http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
									m = Regex.Match(segment, @"(?<rx>[\d\-\.]+)[\s,]+(?<ry>[\d\-\.]+)[\s,]+(?<angle>[\d\-\.]+)[\s,]+(?<large>[\d\-\.]+)[\s,]+(?<sweep>[\d\-\.]+)[\s,]+(?<x>[\d\-\.]+)[\s,]+(?<y>[\d\-\.]+)");
									if (m.Success)
									{
										double rx, ry, angle;
										double.TryParse(m.Groups["rx"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out rx); //Radius X
										double.TryParse(m.Groups["ry"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out ry); //Radius Y
										double.TryParse(m.Groups["angle"].Value, out angle); //Rotation angle
										bool large = (m.Groups["large"].Value.Trim() == "1"); //Flag 1 or 0
										bool sweep = (m.Groups["sweep"].Value.Trim() == "1"); //Flag 1 or 0
										double.TryParse(m.Groups["x"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out x); //End coordinate
										double.TryParse(m.Groups["y"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out y);
										builder.AppendFormat("G03 I{0} J{1} ; arc curve", x.ToString(CultureInfo.InvariantCulture), y.ToString(CultureInfo.InvariantCulture)).AppendLine();
									}
									break;

								case "Z":
									//TODO: Move to start point with G01
									break;

								//default:
								//  throw new NotSupportedException();
							}
						}
						break;
				}

				builder.AppendLine();
			});

			string result = builder.ToString();
			return result;
		}
		#endregion

		#region Protected methods
		protected void FindAllElementsRecursive(XElement root, Action<XElement> callback)
		{
			foreach (var element in root.Elements())
			{
				callback(element);
				FindAllElementsRecursive(element, callback);
			}
		}
		#endregion

		#region IFileConverter Members
		public string Name
		{
			get { return "SVG"; }
		}

		public string Extension
		{
			get { return ".svg"; }
		}

		public bool CanImport
		{
			get { return true; }
		}

		public bool CanExport
		{
			get { return false; }
		}

		public string Import(string path)
		{
			string svg = File.ReadAllText(path);
			//TODO: Check if can import that kind of content
			string gcode = SVGToGCode(svg);
			return gcode;
		}

		public void Export(string path, string content)
		{
			throw new NotSupportedException();

			//string svg = GCodeToSVG(content);
			//File.WriteAllText(path, svg);
		}
		#endregion
	}
}
